package com.kf.bigbluebook.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.kf.bigbluebook.common.ErrorCode;
import com.kf.bigbluebook.constant.UserConstant;
import com.kf.bigbluebook.exception.BusinessException;
import com.kf.bigbluebook.model.po.User;
import com.kf.bigbluebook.mapper.UserMapper;
import com.kf.bigbluebook.service.UserService;

import com.kf.bigbluebook.untils.AlgorithmUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;
import org.apache.commons.math3.util.Pair;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author 86157
 * @description 针对表【user(用户)】的数据库操作Service实现
 * @createDate 2025-01-12 19:09:34
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /***
     * 用户注册
     * @param userAccount
     * @param password
     * @param checkPassword
     * @return
     */
    @Override
    public Long register(String userAccount, String password, String checkPassword) {
        //判断参数是否为空
        if (StringUtils.isBlank(userAccount) || StringUtils.isBlank(password) || StringUtils.isBlank(checkPassword)) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "参数为空");
        }
        //判断账户长度是否正确
        if (userAccount.length() < 4) {
            throw new BusinessException(ErrorCode.PARAM_ERROR, "账户长度不能短于4位");
        }
        //判断密码长度
        if (password.length() < 8 || checkPassword.length() < 8) {
            throw new BusinessException(ErrorCode.PARAM_ERROR, "密码长度不能短于8位");
        }
        //判断账户格式是否正确,账户不能有特殊字符
        String regex = "[`~!@#$%^&*()+=|{}':;',\\\\[\\\\].<>/?~！@#￥%……&*（）——+|{}【】‘；：”“’。，、？]";
        Matcher matcher = Pattern.compile(regex).matcher(userAccount);
        if (matcher.find()) {
            throw new BusinessException(ErrorCode.PARAM_ERROR, "账户不能包含特殊字符");
        }
        //判断两次密码是否一致
        if (!password.equals(checkPassword)) {
            throw new BusinessException(ErrorCode.PARAM_ERROR, "两次密码不一致");
        }
        //查询该账号是否已存在
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userAccount", userAccount);
        Long l = userMapper.selectCount(queryWrapper);
        if (l > 0) {
            throw new BusinessException(ErrorCode.PARAM_ERROR, "该账号已存在");
        }
        //插入新用户
        User user = new User();
        user.setUserAccount(userAccount);
        //对密码进行加密
        String newPassword = DigestUtils.md5DigestAsHex((UserConstant.SALTY + password).getBytes());
        user.setUserPassword(newPassword);
        boolean b = this.save(user);
        if (!b) {
            throw new BusinessException(ErrorCode.PARAM_ERROR, "注册失败");
        }
        return user.getId();


    }

    /***
     * 用户登录
     * @param userAccount
     * @param password
     * @param request
     * @return
     */
    @Override
    public User login(String userAccount, String password, HttpServletRequest request) {
        //判断账号和密码是否为空
        if (StringUtils.isAnyBlank(userAccount, password)) {
            throw new BusinessException(ErrorCode.PARAM_ERROR, "密码或账号为空！");
        }
        //判断账户长度是否正确
        if (userAccount.length() < 4) {
            throw new BusinessException(ErrorCode.PARAM_ERROR, "账户长度不能短于4位");
        }
        //todo 判断密码长度
//        if (password.length() < 8) {
//            throw new BusinessException(ErrorCode.PARAM_ERROR, "密码长度不能短于8位");
//        }
        //判断账户格式是否正确,账户不能有特殊字符
        String regex = "[`~!@#$%^&*()+=|{}':;',\\\\[\\\\].<>/?~！@#￥%……&*（）——+|{}【】‘；：”“’。，、？]";
        Matcher matcher = Pattern.compile(regex).matcher(userAccount);
        if (matcher.find()) {
            throw new BusinessException(ErrorCode.PARAM_ERROR, "账户不能包含特殊字符");
        }
        //对密码进行加密
        String newPassword = DigestUtils.md5DigestAsHex((UserConstant.SALTY + password).getBytes());
        //判断用户密码是否正确
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userAccount", userAccount).eq("userPassword", newPassword);
        User user = userMapper.selectOne(queryWrapper);
        if (user == null) {
            throw new BusinessException(ErrorCode.PARAM_ERROR, "账户或密码错误");
        }
        User safetyUser = getSafetyUser(user);
        request.getSession().setAttribute(UserConstant.USER_LOGIN_STATE, safetyUser);
        return safetyUser;
    }

    /***
     * 去除敏感信息
     * @param originalUser
     * @return
     */
    public User getSafetyUser(User originalUser) {
        if (originalUser == null) {
            return null;
        }
        User user = new User();
        user.setId(originalUser.getId());
        user.setUsername(originalUser.getUsername());
        user.setUserAccount(originalUser.getUserAccount());
        user.setUserRole(originalUser.getUserRole());
        user.setAvatarUrl(originalUser.getAvatarUrl());
        user.setGender(originalUser.getGender());
        user.setPhone(originalUser.getPhone());
        user.setEmail(originalUser.getEmail());
        user.setUpdateTime(originalUser.getUpdateTime());
        user.setCreateTime(originalUser.getCreateTime());
        user.setUserRole(originalUser.getUserRole());
        user.setTags(originalUser.getTags());
        return user;
    }

    /***
     * 注销
     * @param request
     * @return
     */
    @Override
    public int logout(HttpServletRequest request) {
        if (request == null) {
            throw new BusinessException(ErrorCode.PARAM_ERROR);
        }
        request.getSession().removeAttribute(UserConstant.USER_LOGIN_STATE);
        return 1;
    }

    /***
     * 判断是否为管理员
     * @param request
     * @return
     */

    @Override
    public Boolean isAdmin(HttpServletRequest request) {
        Object object = request.getSession().getAttribute(UserConstant.USER_LOGIN_STATE);
        if (object == null) {
            return false;
        }
        User user = (User) object;
        return user.getUserRole() == UserConstant.ADMIN_ROLE;
    }

    /**
     * 根据tags查询用户
     *
     * @param tagNameList
     * @return
     */
    @Override
    public List<User> searchUserByTags(List<String> tagNameList) {
        if (CollectionUtils.isEmpty(tagNameList)) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "tagNameList为空");
        }
        //从redis中获取
        String redisKey = String.format("tags:%s", tagNameList);
        ValueOperations<String, Object> operations = redisTemplate.opsForValue();
        List<User> saftyUserList = (List<User>) operations.get(redisKey);
        if (saftyUserList != null) {
            return saftyUserList;
        }
        log.error("tags redis is null");
        //查询包含对应tag的用户
        //sql查询 select * form user where tag like %.% and like %.%..
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//        for (String tag : tagNameList) {
//            //拼接查询语句
//            queryWrapper = queryWrapper.like("tags",tag);
//        }
//        List<User> userList = userMapper.selectList(queryWrapper);
        //内存查询
        //先查询全部
        Gson gson = new Gson();
        List<User> userList = userMapper.selectList(queryWrapper);
        //使用stream筛选符合条件的数据
        saftyUserList = userList.stream().filter(user -> {
            String tagStr = user.getTags();
            if (StringUtils.isBlank(tagStr)) {
                return false;
            }
            Set<String> tagSet = gson.fromJson(tagStr, new TypeToken<Set<String>>() {
            }.getType());
            //判断tagSet是否为空,为空则取orElse中的值
            tagSet = Optional.ofNullable(tagSet).orElse(new HashSet<>());
            for (String s : tagNameList) {
                //至少包含输入的所有标签才会返回
                if (!tagSet.contains(s))
                    return false;
            }
            return true;
        }).map(this::getSafetyUser).collect(Collectors.toList());
        List<User> users = new ArrayList<>(saftyUserList.subList(0, 20));
        //存入redis中
        try {
            operations.set(redisKey, users, 1, TimeUnit.HOURS);
            log.info("tags redis success");
        } catch (Exception e) {
            log.error("redis tagsKey error", e);
        }
        return users;
    }

    /***
     * 获取当前登录用户
     * @param request
     * @return
     */
    @Override
    public User getLoginUser(HttpServletRequest request) {
        if (request == null) {
            throw new BusinessException(ErrorCode.PARAM_ERROR);
        }
        Object object = request.getSession().getAttribute(UserConstant.USER_LOGIN_STATE);
        if (object == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN);
        }
        return (User) object;
    }

    /***
     * 修改用户信息
     * @param user
     * @param loginUser
     * @return
     */
    @Override
    public int updateUser(User user, User loginUser) {
        //判断参数是否为空
        if (user == null) {
            throw new BusinessException(ErrorCode.PARAM_ERROR);
        }
        log.info(user.getId().toString());
        log.error(loginUser.getId().toString());
        //判断是否有权限，管理员可修改其他用户，非管理员只能修改自己的
        if (!isAdmin(loginUser) && !Objects.equals(user.getId(), loginUser.getId())) {
            throw new BusinessException(ErrorCode.NO_AUTH);
        }
        //判断要修改的用户是否存在
        User user1 = userMapper.selectById(user.getId());
        if (user1 == null) {
            throw new BusinessException(ErrorCode.PARAM_ERROR);
        }
        return userMapper.updateById(user);
    }

    /**
     * 判断传入·用户是否为管理员
     *
     * @param user
     * @return
     */
    @Override
    public Boolean isAdmin(User user) {
        if (user == null) {
            throw new BusinessException(ErrorCode.PARAM_ERROR);
        }
        return user.getUserRole() == UserConstant.ADMIN_ROLE;
    }

    /***
     * 推荐用户
     * @param pageSize
     * @param pageNum
     * @param loginUser
     * @return
     */
    @Override
    public Page<User> pageList(long pageSize, long pageNum, User loginUser) {
        //判断缓存中是否有数据,有则从缓存中获取
        String redisKey = String.format("kf:bigbluebook:userId=%s", loginUser.getId());
        ValueOperations<String, Object> operations = redisTemplate.opsForValue();
        Page<User> userPage = (Page<User>) operations.get(redisKey);
        if (userPage != null) {
            return userPage;
        }
        //没有则从数据中查询
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        userPage = this.page(new Page<User>(pageNum, pageSize), queryWrapper);
        //将查询到的数据放入缓存
        try {
            operations.set(redisKey, userPage, 3000, TimeUnit.SECONDS);
        } catch (Exception e) {
            log.error("redis key error", e);
        }
        return userPage;
    }

    @Override
    public List<User> matchUsers(long num, User loginUser) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("id", "tags");
        queryWrapper.isNotNull("tags");
        List<User> userList = this.list(queryWrapper);
        String tags = loginUser.getTags();
        Gson gson = new Gson();
        List<String> tagList = gson.fromJson(tags, new TypeToken<List<String>>() {
        }.getType());
        // 用户列表的下标 => 相似度
        List<Pair<User, Long>> list = new ArrayList<>();
        // 依次计算所有用户和当前用户的相似度
        for (int i = 0; i < userList.size(); i++) {
            User user = userList.get(i);
            String userTags = user.getTags();
            // 无标签或者为当前用户自己
            if (StringUtils.isBlank(userTags) || user.getId() == loginUser.getId()) {
                continue;
            }
            List<String> userTagList = gson.fromJson(userTags, new TypeToken<List<String>>() {
            }.getType());
            // 计算分数
            long distance = AlgorithmUtils.minDistance(tagList, userTagList);
            list.add(new Pair<>(user, distance));
        }
        // 按编辑距离由小到大排序
        List<Pair<User, Long>> topUserPairList = list.stream()
                .sorted((a, b) -> (int) (a.getValue() - b.getValue()))
                .limit(num)
                .collect(Collectors.toList());
        // 原本顺序的 userId 列表
        List<Long> userIdList = topUserPairList.stream().map(pair -> pair.getKey().getId()).collect(Collectors.toList());
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.in("id", userIdList);
        // 1, 3, 2
        // User1、User2、User3
        // 1 => User1, 2 => User2, 3 => User3
        Map<Long, List<User>> userIdUserListMap = this.list(userQueryWrapper)
                .stream()
                .map(user -> getSafetyUser(user))
                .collect(Collectors.groupingBy(User::getId));
        List<User> finalUserList = new ArrayList<>();
        for (Long userId : userIdList) {
            finalUserList.add(userIdUserListMap.get(userId).get(0));
        }
        return finalUserList;
    }
}




